package com.example;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.BinaryOperator;
import java.util.function.Consumer;
import java.util.function.ToIntFunction;
import java.util.stream.Stream;

/**
 * @author twj
 * @description 方法引用案例
 * @time 2023/9/3^15:31:51
 */
public class MethodReference {
    public static void main(String[] args) {
        /*
         * 我们在使用lambda时，如果方法体中只有一个方法的调用的话（包括构造方法），我们可以使用方法引用进一步简化代码。
         * 基本格式：类名或对象名::方法名
         */

        /* 1-引用类的静态方法
         * 如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个类的静态方法，并且我们把要重写的抽象方法中所
         * 有的参数都按照顺序传入了这个静态方法中，这个时候我们就可以引用类的静态方法。
         */
        Stream<Author> stream = getAuthors().stream();
        stream.map(author -> author.getAge())
                .map(String::valueOf);

        /* 2-引用对象的实例方法
         * 如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个对象的成员方法，并且我们把要重写的抽象方法中所
         * 有的参数都按照顺序传入了这个成员方法中，这个时候我们就可以引用对象的实例方法
         */
        Stream<Author> stream1 = getAuthors().stream();
        StringBuilder sb = new StringBuilder();
        stream1.map(author -> author.getAge())
                .forEach(sb::append);

        /* 3-引用类的实例方法
         * 如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了第一个参数的成员方法，并且我们把要重写的抽象方法
         * 中剩余的所有的参数都按照顺序传入了这个成员方法中，这个时候我们就可以引用类的实例方法。
         */
        subAuthorName("哇哈哈王老吉", String::substring);

        /* 4-构造器引用
         * 如果我们在重写方法的时候，方法体中只有一行代码，并且这行代码是调用了某个类的构造方法，并且我们把要重写的抽象方法中
         * 的所有的参数都按照顺序传入了这个构造方法中，这个时候我们就可以引用构造器。
         */
        Stream<Author> stream2 = getAuthors().stream();
        stream2.map(Author::getName)
                .map(StringBuilder::new)
                .map(s -> s.append("哇哈哈").toString())
                .forEach(System.out::println);

        /*
         * 基本数据类型优化！！！！！！！！！！！！！
         *   我们之前用到的很多Stream的方法由于都使用了泛型。所以涉及到的参数和返回值都是引用数据类型。
         *   即使我们操作的是整数小数，但是实际用的都是他们的包装类。JDK5中引入的自动装箱和自动拆箱让我们在使用对应的包装类时
         *   就好像使用基本数据类型一样方便。但是我们一定要知道装箱和拆箱肯定是要消耗时间的。虽然这个时间消耗很小。
         *   但是在大量的数据不断的重复装箱拆箱的时候，我们就不能无视这个时间损耗了。
         *   所以为了让我们能够对这部分的时间消耗进行优化。Stream还提供了很多专门针对基本数据类型的方法。
         *   例如: mapToInt,mapToLong,mapToDouble,flatMapToInt,flatMapToDouble等。
         */
        Stream<Author> stream3 = getAuthors().stream();
        stream3.map(Author::getAge)
                .map(age -> age + 10)
                .filter(age -> age > 18)
                .map(age -> age + 2)
                .forEach(System.out::println);

        Stream<Author> stream4 = getAuthors().stream();
        stream4.mapToInt(author -> author.getAge())
                .map(age -> age + 10)
                .filter(age -> age > 18)
                .map(age -> age + 2)
                .forEach(System.out::println);

        /* 并行流！！！！！！！！！！
         *   当流中有大量元素时，我们可以使用并行流去提高操作的效率。其实并行流就是把任务分配给多个线程去完成，
         *   如果我们自己去用代码实现的话其实会非常的复杂，并且要求我们对并发编程有足够的理解和认识，
         *   而如果我们使用Stream的话，我们只需要修改一个方法的调用就可以使用并行流来帮我们实现，从而提高效率。
         *
         * parallel() 方法可以把串行流转换成并行流
         * parallelStream() 方法用于直接获取并行流对象
         * peek() 方法用来做一些调试操作
         *
         */

        Stream<Integer> stream5 = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        Integer sum = stream5.parallel()
                .peek(new Consumer<Integer>() {
                    @Override
                    public void accept(Integer num) {
                        System.out.println("线程名: " + Thread.currentThread().getName() + " 当前值: " + num);
                    }
                })
                .filter(num -> num > 5)
                .reduce((result, element) -> result + element)
                .get();
        System.out.println(sum);


    }

    interface UseString {
        String use(String str, int start, int length);
    }

    public static String subAuthorName(String str, UseString useString) {
        int start = 0, length = 1;
        return useString.use(str, start, length);
    }

    private static List<Author> getAuthors() {
        //数据初始化
        Author author = new Author(1L, "蒙多", 33, "一个从菜刀中明悟哲理的祖安人", null);
        Author author2 = new Author(2L, "亚拉索", 15, "狂风也追逐不上他的思考速度", null);
        Author author3 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);
        Author author4 = new Author(3L, "易", 14, "是这个世界在限制他的思维", null);

        //书籍列表
        List<Book> books1 = new ArrayList();
        List<Book> books2 = new ArrayList();
        List<Book> books3 = new ArrayList();

        books1.add(new Book(1L, "刀的两侧是光明与黑暗", "哲学,爱情", 88, "用一把刀划分了爱恨"));
        books1.add(new Book(2L, "一个人不能死在同一把刀下", "个人成长,爱情", 99, "讲述如何从失败中明悟真理"));

        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(3L, "那风吹不到的地方", "哲学", 85, "带你用思维去领略世界的尽头"));
        books2.add(new Book(4L, "吹或不吹", "爱情,个人传记", 56, "一个哲学家的恋爱观注定很难把他所在的时代理解"));

        books3.add(new Book(5L, "你的剑就是我的剑", "爱情", 56, "无法想象一个武者能对他的伴侣这么的宽容"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢？"));
        books3.add(new Book(6L, "风与剑", "个人传记", 100, "两个哲学家灵魂和肉体的碰撞会激起怎么样的火花呢？"));

        author.setBooks(books1);
        author2.setBooks(books2);
        author3.setBooks(books3);
        author4.setBooks(books3);

        List<Author> authorList = new ArrayList(Arrays.asList(author, author2, author3, author4));
        return authorList;
    }
}
